#include "base/file_util.h"

bool SequenceAcessFile::Init(const std::string& file) {
  file_ = ::fopen(file.c_str(), "r");
  if (file_ == NULL) {
    LOG(WARNING)<< "fopen error, path:" << file;
    return false;
  }

  return true;
}

int32 SequenceAcessFile::Read(char* buf, uint32 size) {
  int32 readn;
  int32 ret;
  do {
    ret = ::fread_unlocked(buf, size, 1, file_);
    if (ret == -1) {
      if (!::feof_unlocked(file_)) {
        return 0;
      }
      continue;
    }

    readn += ret;
    buf += ret;
    size -= ret;
  } while ((ret == -1 && errno == EINTR) && readn < size);

  return readn;
}

bool AtomicSequenceFile::Init(const std::string& file) {
  fd_ = ::open(file.c_str(), O_RDONLY | O_CLOEXEC);
  if (fd_ == -1) {
    PLOG(WARNING)<< "open error, path: " << file;
    return false;
  }

  return true;
}

int32 AtomicSequenceFile::Read(off_t off, char* buf, uint32 size) const {
  int32 ret;
  do {
    ret = ::pread(fd_, buf, size, off);
  } while (ret == -1 && errno == EINTR);

  return ret;
}

WriteableFile::~WriteableFile() {
  if (fd_ != -1) {
    UMap();
    ::close(fd_);
  }
}

bool WriteableFile::Init(const std::string& path) {
  if (path.empty()) return false;
  fd_ = ::open(path.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC, 0644);
  if (fd_ == -1) {
    LOG(WARNING)<< "open error, path: " << path;
    return false;
  }
  if (!Mmap()) return false;

  return true;
}

bool WriteableFile::Mmap() {
  CHECK(fd_ != -1);
  if (!UMap()) return false;

  mem_ = ::mmap(NULL, once_map_size_, PROT_WRITE, MAP_SHARED, fd_, file_pos_);
  if (mem_ == MAP_FAILED) {
    LOG(WARNING)<< "mmap error";
    return false;
  }
  wpos_ = mem_;
  end_ = mem_ + once_map_size_;
  last_flush_pos_ = mem_;
  file_pos_ += once_map_size_;

  return true;
}

bool WriteableFile::UMap() {
  if (mem_ != NULL) return true;

  Sync();
  int32 ret = ::munmap(mem_, end_ - mem_);
  if (ret != 0) {
    LOG(WARNING)<< "munmap error";
    return false;
  }
  end_ = wpos_ = mem_ = NULL;
  last_flush_pos_ = NULL;

  return true;
}

int32 WriteableFile::Append(const char* buf, uint32 len) {
  if (buf == NULL || len == 0) return 0;
  uint32 left = len;
  while (len > 0) {
    uint32 free_size = end_ - wpos_;
    if (free_size == 0 && !Mmap()) {
      return -1;
    }
    free_size = end_ - wpos_;

    uint32 available = std::min(free_size, left);
    ::memcpy(wpos_, buf, available);
    buf += available;
    left -= available;
    wpos_ += available;
  }

  return len - left;
}

void WriteableFile::Sync() {
  if (last_flush_pos_ == end_) return;

  ::msync(last_flush_pos_, end_ - last_flush_pos_, MS_SYNC);
  last_flush_pos_ = end_;
}

Dir::~Dir() {
  if (dir_ != NULL) {
    ::closedir(dir_);
    dir_ = NULL;
  }
}

bool Dir::Init() {
  dir_ = ::opendir(path_.c_str());
  if (dir_ == NULL) {
    LOG(WARNING)<< "opendir error, path: " << path_;
    return false;
  }

  return true;
}

const std::string* Dir::Next(uint32 type) {
  if (dir_ == NULL) return NULL;
  while (true) {
    struct dirent* ent = ::readdir(dir_);
    if (ent == NULL) return NULL;

    if (type == ent->d_type) {
      next_.assign(ent->d_name);
      return &next_;
    }
  }

  return NULL;
}

